<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>ORM - Oatpp教程</title>
        <!-- Custom HTML head -->
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="favicon.svg">
        <link rel="shortcut icon" href="favicon.png">
        <link rel="stylesheet" href="css/variables.css">
        <link rel="stylesheet" href="css/general.css">
        <link rel="stylesheet" href="css/chrome.css">
        <link rel="stylesheet" href="css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="highlight.css">
        <link rel="stylesheet" href="tomorrow-night.css">
        <link rel="stylesheet" href="ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script>
            var path_to_root = "";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script>
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script>
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script>
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded "><a href="DTO.html"><strong aria-hidden="true">1.</strong> DTO</a></li><li class="chapter-item expanded "><a href="Async概述.html"><strong aria-hidden="true">2.</strong> Async</a></li><li class="chapter-item expanded "><a href="Simple_vs_Async.html"><strong aria-hidden="true">3.</strong> Simple vs Async</a></li><li class="chapter-item expanded "><a href="协程.html"><strong aria-hidden="true">4.</strong> 协程</a></li><li class="chapter-item expanded "><a href="关系型对象映射框架.html" class="active"><strong aria-hidden="true">5.</strong> ORM</a></li><li class="chapter-item expanded "><a href="Monolithization.html"><strong aria-hidden="true">6.</strong> Monolithization</a></li><li class="chapter-item expanded "><a href="步骤指南.html"><strong aria-hidden="true">7.</strong> 步骤指南</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Oatpp教程</h1>

                    <div class="right-buttons">
                        <a href="print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script>
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="关系型对象映射框架"><a class="header" href="#关系型对象映射框架">关系型对象映射框架</a></h1>
<p>Oat++ ORM框架采用广义的接口并且使数据库操作变得容易</p>
<p>这基于对象映射框架并且保证操作过程中的数据一致性。另外ORM能够很好的结合进其他Oat++组件，确保在应用程序中数据流的无缝衔接。（比如从REST到数据库，或者从数据库到REST）</p>
<h2 id="高层次api概览"><a class="header" href="#高层次api概览">高层次API概览</a></h2>
<h3 id="声明dbclient"><a class="header" href="#声明dbclient">声明DbClient</a></h3>
<p>你将使用的主要组件是DbClient，主要在该组件上处理数据，声明数据库请求并且管理数据库版本。</p>
<p>使用代码生成宏辅助生成数据库请求</p>
<p>DbClient代码创建块必须以<code>#include OATPP_CODEGEN_BEGIN(DbClient)</code> 并且必须用<code>#include OATPP_CODEGEN_END(DbClient)</code> 关闭代码块</p>
<p><em>不要忘记关闭代码生成块以防止在接下来的代码中出现宏定义冲突</em></p>
<pre><code class="language-text-x-c-src">#include &quot;oatpp/orm/SchemaMigration.hpp&quot;
#include &quot;oatpp/orm/DbClient.hpp&quot;
#include &quot;oatpp/core/macro/codegen.hpp&quot;

#include OATPP_CODEGEN_BEGIN(DbClient) ///&lt; Begin code-gen section

class MyClient : public oatpp::orm::DbClient {
public:

  MyClient(const std::shared_ptr&lt;oatpp::orm::Executor&gt;&amp; executor)
    : oatpp::orm::DbClient(executor)
  {}
  
  QUERY(createUser,
        &quot;INSERT INTO users (username, email, role) VALUES (:username, :email, :role);&quot;,
        PARAM(oatpp::String, username), 
        PARAM(oatpp::String, email), 
        PARAM(oatpp::Enum&lt;UserRoles&gt;::AsString, role)) 

  QUERY(getUserByName, 
        &quot;SELECT * FROM users WHERE username=:username;&quot;, 
        PARAM(oatpp::String, username)) 
        
};

#include OATPP_CODEGEN_END(DbClient) ///&lt; End code-gen section
</code></pre>
<p><strong>创建DbClient组建并且连接到数据库</strong></p>
<p>DbClient是一个重量级的对象，一般只需要声明一次，然后在任何需要的地方引用即可</p>
<pre><code class="language-text-x-c-src">#include &quot;db/MyClient.hpp&quot;      //用户定义的DbClient
#include &quot;oatpp-sqlite/orm.hpp&quot; //&lt; 适用于Oat++ ORM的SQLite适配器

class AppComponent {
public:
  
  //创建DbCLient组建
  //这里使用SQLite作为例子，其他数据库的声明都是类似的
  OATPP_CREATE_COMPONENT(std::shared_ptr&lt;db::MyClient&gt;, myDatabaseClient)([] {

    //创建数据库模板特化的ConnectProvider
    auto connectionProvider = std::make_shared&lt;oatpp::sqlite::ConnectionProvider&gt;(&quot;/path/to/database.sqlite&quot;);    

    //创建数据库模板特化的执行器
    auto executor = std::make_shared&lt;oatpp::sqlite::Executor&gt;(connectionProvider);
  
    //创建MyClient数据库连接
    return std::make_shared&lt;MyClient&gt;(executor);

  }());

};
</code></pre>
<blockquote>
<p>ConnectionProvider和ConnectionPool对象可以被多个Executors重复使用，除非数据库的模板特化对象被禁止</p>
<p>Executor可以被多个DbClients重复使用，除非数据库的模板特化对象被禁止</p>
</blockquote>
<h3 id="dbclient使用例子"><a class="header" href="#dbclient使用例子">DbClient使用例子</a></h3>
<pre><code class="language-text-x-c-src">//添加MyClient的数据库连接
OATPP_COMPONENT(std::shared_ptr&lt;db::MyClient&gt;, client);

//在数据库中添加新用户
client-&gt;createUser(&quot;admin&quot;, &quot;admin@domain.com&quot;, UserRoles::ADMIN);

//在数据库中寻找用户名
auto result = client-&gt;getUserByUsername(&quot;admin&quot;);

//接收返回结果为UserDto模板类型的vector
//在此之前，UserDto已经被定义过了
//你也可以使用oatpp::Fields&lt;oatpp::Any&gt;来替代oatpp::Object&lt;UserDto&gt;接收任意类型的结果
auto dataset = result-&gt;fetch&lt;oatpp::Vector&lt;oatpp::Object&lt;UserDto&gt;&gt;&gt;();

//并且我们也可以很容易的使用JSON对象映射将结果序列化为JSON字符串
auto json = jsonObjectMapper.writeToString(dataset);

//打印JSON结果
std::cout &lt;&lt; json-&gt;c_str() &lt;&lt; std::endl;
</code></pre>
<p>输出</p>
<pre><code class="language-application-json">[
  {
    &quot;name&quot;: &quot;admin&quot;,
    &quot;email&quot;: &quot;admin@domain.com&quot;,
    &quot;role&quot;: &quot;ROLE_ADMIN&quot;
  }
]
</code></pre>
<h2 id="支持的数据库"><a class="header" href="#支持的数据库">支持的数据库</a></h2>
<h3 id="可用的数据库适配器"><a class="header" href="#可用的数据库适配器">可用的数据库适配器</a></h3>
<div class="table-wrapper"><table><thead><tr><th></th><th></th><th></th><th></th></tr></thead><tbody>
<tr><td>适配器</td><td>数据库</td><td>限制</td><td>示例项目</td></tr>
<tr><td>oatpp-sqlite</td><td>SQLite</td><td>全特性支持</td><td>example-crud</td></tr>
<tr><td>oatpp-postgresql</td><td>PostgreSQL</td><td>全部的postgres类型都不支持</td><td>example-postgresql</td></tr>
</tbody></table>
</div>
<h3 id="库目录树"><a class="header" href="#库目录树">库目录树</a></h3>
<p>主要的oatpp模块仅包含ORM接口，为了连接各种特定的数据库，你需要连接相关的适配器</p>
<pre><code class="language-application-json">- oatpp                     #主oatpp模块，ORM接口在这里
    |
    |- oatpp-sqlite         # Oatpp ORM的Sqlite适配器,Sqlite的模板特例化实现在这里
    |- oatpp-postgresql     # Oatpp ORM的PostgreSQL适配器,PostgreSQL的模板特化实现在这里
    ...
    ... etc.
</code></pre>
<h2 id="dbclient"><a class="header" href="#dbclient">DbClient</a></h2>
<h3 id="定义一个查询"><a class="header" href="#定义一个查询">定义一个查询</a></h3>
<pre><code class="language-text-x-c-src">QUERY(selectAllUsers, &quot;SELECT * FROM users;&quot;)
</code></pre>
<h3 id="带参数查询"><a class="header" href="#带参数查询">带参数查询</a></h3>
<pre><code class="language-text-x-c-src">QUERY(selectUserByUsername, 
      &quot;SELECT * FROM users WHERE username=:username;&quot;,
      PARAM(oatpp::String, username)) 
</code></pre>
<p>在执行过程中，表达式<code>username=:username</code> 将会被改为<code>username='&lt;username-parameter-value&gt;'</code> 并且参数的值将会</p>
<p>正确的转化为相符的类型。</p>
<h3 id="带dto参数的查询"><a class="header" href="#带dto参数的查询">带DTO参数的查询</a></h3>
<p>在复杂的请求中，使用DTO对象作为参数能带来很多便利，因此你需要确保正确的参数顺序</p>
<pre><code class="language-text-x-c-src">QUERY(insertUser, 
      &quot;INSERT INTO users &quot;
      &quot;(username, email, role) VALUES &quot;
      &quot;(:user.username, :user.email, :user.role);&quot;,
      PARAM(oatpp::Object&lt;UserDto&gt;, user))
</code></pre>
<blockquote>
<p>这个请求模板的变量名称从user开始</p>
<p>比如user.username中，user为DTO参数名称，username为DTO字段名称</p>
</blockquote>
<ul>
<li> 你可以使用具体的路径指定嵌套DTO字段，比如<code>:user.path.to.nested.field</code></li>
<li>你可以在请求中包含多个DTO参数，并且你也可以在普通的参数中混合DTO参数</li>
</ul>
<h3 id="带预编译语句的请求"><a class="header" href="#带预编译语句的请求">带预编译语句的请求</a></h3>
<pre><code class="language-text-x-c-src">QUERY(selectUserByUsername, 
      &quot;SELECT * FROM users WHERE username=:username;&quot;,
      PREPARE(true), //设置prepare为true以使用预编译语句
      PARAM(oatpp::String, username)) 
</code></pre>
<blockquote>
<p>数据库适配器可能会忽略这个语句，比如说：</p>
<ul>
<li>SQLite一直使用预编译语句执行请求，因此oatpp-sqlite会忽略PREPARE参数</li>
<li>PostgreSQL采用特殊的方法去执行预编译语句，因此oatpp-postgresql将不会忽略PREPARE参数</li>
</ul>
</blockquote>
<h3 id="执行一个任意的查询"><a class="header" href="#执行一个任意的查询">执行一个任意的查询</a></h3>
<p>执行一个任意的查询使用<code>DbClient::executeQuery()</code> 这个方法</p>
<p>当需要构建一个动态的查询就使用这个方法</p>
<pre><code class="language-text-x-c-src">auto dbResult = client.executeQuery(&quot;SELECT * FROM users;&quot;, {} /* 空参数表 */);
</code></pre>
<p>你可以使用参数表添加参数</p>
<pre><code class="language-text-x-c-src">auto dbResult = client.executeQuery(
  &quot;SELECT * FROM users WHERE id=:id AND username=:username;&quot;, 
  {
    {&quot;id&quot;, oatpp::Int64(23)},             //你可以直接在这里指定参数类型 - oatpp::Int64
    {&quot;username&quot;, oatpp::String(&quot;admin&quot;)}
  }
);
</code></pre>
<p>当需要动态构建参数表时可以使用<code>std::unordered_map::insert()</code> 方法，<code>[]</code> 操作在这里不可用</p>
<pre><code class="language-text-x-c-src">std::unordered_map&lt;oatpp::String, oatpp::Void&gt; params;
params.insert({&quot;id&quot;, oatpp::Int64(23)});
params.insert({&quot;username&quot;, oatpp::String(&quot;admin&quot;)});
auto dbResult = client.executeQuery(&quot;SELECT * FROM users WHERE id=:id AND username=:username;&quot;, params);
</code></pre>
<p>构建查询字符串推荐使用<code>oatpp::data::stream::BufferOutputStream</code></p>
<pre><code class="language-text-x-c-src">#include &quot;oatpp/core/data/stream/BufferStream.hpp&quot;

...

oatpp::data::stream::BufferOutputStream stream;
stream 
&lt;&lt; &quot;SELECT * FROM users &quot;
&lt;&lt; &quot;WHERE &quot;
&lt;&lt; &quot;id=:id&quot; &lt;&lt; &quot; AND &quot; &lt;&lt; &quot;username=:username&quot; &lt;&lt; &quot;;&quot; 

std::unordered_map&lt;oatpp::String, oatpp::Void&gt; params;
params.insert({&quot;id&quot;, oatpp::Int64(23)});
params.insert({&quot;username&quot;, oatpp::String(&quot;admin&quot;)});

auto dbResult = client.executeQuery(stream.toString(), params);
</code></pre>
<h3 id="启用类型解释type-interpretations"><a class="header" href="#启用类型解释type-interpretations">启用类型解释（Type Interpretations）</a></h3>
<p>当在<code>QUERY</code> 宏参数列表中使用自定义或非标准类型或者在读取查询的结果是自定义或非标准结构，你应该明确的使用相应的类型解释</p>
<p>一个推荐的做法是使用构造函数</p>
<pre><code class="language-text-x-c-src">class MyClient : public oatpp::orm::DbClient {
public:

  MyClient(const std::shared_ptr&lt;oatpp::orm::Executor&gt;&amp; executor)
    : oatpp::orm::DbClient(executor)
  {
    setEnabledInterpretations({&quot;protobuf&quot;});
  }

  ...
        
};
</code></pre>
<p><strong>采用自定义类型参数的查询</strong></p>
<pre><code class="language-text-x-c-src">QUERY(insertUser, 
      &quot;INSERT INTO users &quot;
      &quot;(username, email, role) VALUES &quot;
      &quot;(:user.username, :user.email, :user.role);&quot;,
      PARAM(oatpp::protobuf::Object&lt;User&gt;, user)) //通过protobuf对象
</code></pre>
<p><strong>映射查询结果到自定义类型</strong></p>
<pre><code class="language-text-x-c-src">/*执行查询*/
auto result = client-&gt;getUserByUsername(&quot;admin&quot;);

/*映射结果到protobuf对象到vector容器中*/
auto dataset = res-&gt;fetch&lt;oatpp::Vector&lt;oatpp::protobuf::Object&lt;User&gt;&gt;&gt;(); // Map result

for(auto&amp; user : *dataset) {
  ...
}
</code></pre>
<p><strong>处理</strong></p>
<p>使用<code>DbClient::beginTransaction()</code> 方法开始处理</p>
<p>所有的请求必须被同一个处理连接执行</p>
<pre><code class="language-text-x-c-src">{
  auto transaction = client.beginTransaction();
  
  client.insertUser(user1, transaction.getConnection());
  client.insertUser(user2, transaction.getConnection());
  client.insertUser(user3, transaction.getConnection());

  transaction.commit();
}
</code></pre>
<blockquote>
<p>如果<code>Transaction::commit()</code> 没有被调用，那么这些处理将会被自动回滚</p>
</blockquote>
<h2 id="执行查询"><a class="header" href="#执行查询">执行查询</a></h2>
<pre><code class="language-text-x-c-src">/* Execute a query */
auto queryResult = client.selectAllUsers();

/* Check if the operation was successful */
if(!queryResult-&gt;isSuccess()) {
  auto message = queryResult-&gt;getErrorMessage();
  OATPP_LOGD(&quot;Query&quot;, &quot;Error, message=%s&quot;, message-&gt;c_str());
}

/* Fetch everything as a vector of User objects */
auto dataset = queryResult-&gt;fetch&lt;oatpp::Vector&lt;oatpp::Object&lt;User&gt;&gt;&gt;();
</code></pre>
<p><code>queryResult</code> 在这里是<code>oatpp::orm::QueryResult</code> 对象，所有的请求都会返回<code>oatpp::orm::QueryResult</code></p>
<h3 id="映射请求"><a class="header" href="#映射请求">映射请求</a></h3>
<p>可用的请求映射取决于数据库适配器，但是这里有一些例子（这些例子能够工作在oatpp-sqlite和oatpp-postgresql）</p>
<p>无论映射什么都需要提前声明<code>UserDto</code> 并显示结果</p>
<p>更多关于声明DTO的信息，参见<a href="https://oatpp.io/docs/components/dto/">oatpp::DTO</a></p>
<pre><code class="language-text-x-c-src">auto dataset = queryResult-&gt;fetch&lt;oatpp::Vector&lt;oatpp::Object&lt;oatpp::UserDto&gt;&gt;&gt;();

/* Serialize result as a json string using json object mapper */
auto json = jsonObjectMapper.writeToString(dataset);

/* Print the resultant json */
std::cout &lt;&lt; json-&gt;c_str() &lt;&lt; std::endl;
</code></pre>
<p>输出</p>
<pre><code class="language-text-x-c-src">[
  {
    &quot;name&quot;: &quot;admin&quot;,
    &quot;email&quot;: &quot;admin@domain.com&quot;,
    &quot;role&quot;: &quot;ROLE_ADMIN&quot;
  },
  {
    &quot;name&quot;: &quot;ivan&quot;,
    &quot;email&quot;: &quot;ivan@domain.com&quot;,
    &quot;role&quot;: &quot;ROLE_GUEST&quot;
  }
]
</code></pre>
<p>使用<code>oatpp::Any</code> 映射所有数据并且显示结果</p>
<pre><code class="language-text-x-c-src">auto dataset = queryResult-&gt;fetch&lt;oatpp::Vector&lt;oatpp::Fields&lt;oatpp::Any&gt;&gt;&gt;();

/* Serialize result as a json string using json object mapper */
auto json = jsonObjectMapper.writeToString(dataset);

/* Print the resultant json */
std::cout &lt;&lt; json-&gt;c_str() &lt;&lt; std::endl;
</code></pre>
<p>输出</p>
<pre><code class="language-text-x-c-src">[
  {
    &quot;name&quot;: &quot;admin&quot;,
    &quot;email&quot;: &quot;admin@domain.com&quot;,
    &quot;role&quot;: &quot;ROLE_ADMIN&quot;
  },
  {
    &quot;name&quot;: &quot;ivan&quot;,
    &quot;email&quot;: &quot;ivan@domain.com&quot;,
    &quot;role&quot;: &quot;ROLE_GUEST&quot;
  }
]
</code></pre>
<h3 id="管理连接"><a class="header" href="#管理连接">管理连接</a></h3>
<p>所有声明的请求最后一个参数都是<code>oatpp::orm::Connection</code></p>
<p>如果没有指定连接（<code>nullptr</code>）那么新的连接将会在执行请求时打开</p>
<pre><code class="language-text-x-c-src">{
  auto queryResult = client.selectAllUsers(); //&lt; Open a new connection.
}

{
  auto connection = client.getConnection();
  auto queryResult = client.selectAllUsers(connection); //&lt; Execute using the connection provided.
}

{
  auto queryResult = client.selectAllUsers(); //&lt; Open a new connection.
  ...
  queryResult = client.insertUser(user, queryResult-&gt;getConnection()); //&lt; Execute on the same connection as the last query.
}
</code></pre>
<blockquote>
<p>queryResult会保持一个连接，这个连接不会返回到连接池中，除非queryResult被销毁了</p>
</blockquote>
<h2 id="连接池"><a class="header" href="#连接池">连接池</a></h2>
<p>当使用数据库时，这里有一个好的办法去使用连接池</p>
<pre><code class="language-text-x-c-src">#include &quot;db/MyClient.hpp&quot;
#include &quot;oatpp-sqlite/orm.hpp&quot;

class AppComponent {
public:
  
  /**
   * Create DbClient component.
   * SQLite is used as an example here. For other databases declaration is similar.
   */
  OATPP_CREATE_COMPONENT(std::shared_ptr&lt;db::MyClient&gt;, myDatabaseClient)([] {
    /* Create database-specific ConnectionProvider */
    auto connectionProvider = std::make_shared&lt;oatpp::sqlite::ConnectionProvider&gt;(&quot;/path/to/database.sqlite&quot;);    
  
    /* Create database-specific ConnectionPool */
    auto connectionPool = oatpp::sqlite::ConnectionPool::createShared(connectionProvider, 
                                                                      10 /* max-connections */, 
                                                                      std::chrono::seconds(5) /* connection TTL */);
    
    /* Create database-specific Executor */
    auto executor = std::make_shared&lt;oatpp::sqlite::Executor&gt;(connectionPool);
  
    /* Create MyClient database client */
    return std::make_shared&lt;MyClient&gt;(executor);
  }());

};
</code></pre>
<blockquote>
<p>这个例子中使用的是SQLite，其他数据库的声明都是类似的</p>
</blockquote>
<h2 id="schema-migration"><a class="header" href="#schema-migration">Schema Migration</a></h2>
<p>使用<a href="https://oatpp.io/api/latest/oatpp/orm/SchemaMigration/">SchemaMigration</a> 完成结构迁移</p>
<p>推荐的做法是使用Schema Migration构造DbClient</p>
<h3 id="概览"><a class="header" href="#概览">概览</a></h3>
<pre><code class="language-text-x-c-src">class MyClient : public oatpp::orm::DbClient {
public:

  MyClient(const std::shared_ptr&lt;oatpp::orm::Executor&gt;&amp; executor)
    : oatpp::orm::DbClient(executor)
  {
    oatpp::orm::SchemaMigration migration(executor); 
    migration.addFile(1 /* version */, &quot;sql/initial_schema.sql&quot; /* migration script */);
    migration.addFile(2 /* version */, &quot;sql/schema_fix_1.sql&quot;   /* migration script */);
    ...
    migration.migrate(); //&lt;-- This guy will throw on error.
  }

  ...
        
};
</code></pre>
<blockquote>
<ul>
<li>版本必须从1开始</li>
<li>版本必须从1开始增加</li>
<li>如果有错误发生，将会回滚到最后一次成功应用的版本</li>
</ul>
</blockquote>
<h3 id="schema名称"><a class="header" href="#schema名称">Schema名称</a></h3>
<p>如果你有多个Schemans在你的数据库中，你可以单独的管理每一个migrations，采用指定的版本控制前缀：</p>
<pre><code class="language-text-x-c-src">oatpp::orm::SchemaMigration migration(executor, &quot;suffix&quot;);
</code></pre>
<blockquote>
<p>推荐每个schema都有一个DbClient</p>
</blockquote>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="协程.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                            <a rel="next" href="Monolithization.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="协程.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    <a rel="next" href="Monolithization.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>

        <!-- Livereload script (if served using the cli tool) -->
        <script>
            const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
            const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
            const socket = new WebSocket(wsAddress);
            socket.onmessage = function (event) {
                if (event.data === "reload") {
                    socket.close();
                    location.reload();
                }
            };

            window.onbeforeunload = function() {
                socket.close();
            }
        </script>
        <script>
            window.playground_copyable = true;
        </script>
        <script src="elasticlunr.min.js"></script>
        <script src="mark.min.js"></script>
        <script src="searcher.js"></script>
        <script src="clipboard.min.js"></script>
        <script src="highlight.js"></script>
        <script src="book.js"></script>

        <!-- Custom JS scripts -->
    </body>
</html>
